﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.MemoryMappedFiles;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;


    ///<summary> /// This class represts an matrix of unlimited size. This matrix is stored into a Memory Mapped File. 
    ///</summary> 
   public class MemoryMappedMatrix
    {
        long tami;
        long tamj;
        long cellsize;
        String uniqueName = "pagf-" + Guid.NewGuid();
        string path;
        string fileName;
        long Lenght;
        long size;
        MemoryMappedFile mmf;

        ///<summary> 
        /// Constructor method for the mmfMatrix. 
        ///</summary> 
        ///<param name="cellsize">
        /// Number of char`s of the desired cell.
        ///</param>
        ///<param name="tami">
        /// Number of lines of the matrix.
        ///</param> 
        ///<param name="tamj">
        /// Number of columns of the matrix.
        ///</param> 
        ///<param name="path">
        /// Path where the memory mapped file should be stored.
        ///</param> 
         public MemoryMappedMatrix(long cellsize, long tami,long tamj, string path)
        {
             //initialize the main variables
            this.path = path;
            this.tami = tami;
            this.tamj = tamj;
            this.cellsize = cellsize;
            this.Lenght = tami * tamj;
            Int64 sizex = tami * tamj;
            this.size = ((tami * tamj)*cellsize)+(long)1;
            //create the mainfileName.
            this.fileName = Path.Combine(path, uniqueName + ".bin");
            //create the MMF file.
            CreateMMF();
            
        }
         ///<summary> 
         /// This method creates an mmf file on the desired path. 
         ///</summary> 
         public void CreateMMF()
         {
            

             mmf = MemoryMappedFile.CreateFromFile(File.Create(fileName), uniqueName, size, MemoryMappedFileAccess.ReadWrite, null, HandleInheritability.Inheritable, false);
            
         }
         ///<summary> 
         /// This method reads the contend of an cell of the matrix, guiven the i and j position.(Best method for any size) 
         ///</summary> 
         ///<param name="posi">
         /// Line position of the desired cell.
         ///</param>
         ///<param name="posj">
         /// Column position of the desired cell.
         ///</param>
         public String getPosition(long posi, long posj)
        {
             //initialize the cell data.
            String cell = "";

             //calculates the line offset position.
            long linePosition = posi*(tamj*cellsize);
             //calculates the column offset position.
            long columnPosition = posj * cellsize;
             //calculates the offset position of the cell
            long offset = linePosition + columnPosition;
             //initializes the buffer of the reader
            byte[] buffer = new byte[cellsize];
             //creates a mmf view acessor for reading
            MemoryMappedViewAccessor reader = mmf.CreateViewAccessor(offset, cellsize+1);
             //read the data into the buffer
            for (long x = 0; x < cellsize; x++)
            {
                
                buffer[x] = reader.ReadByte(x);
                 
            }
             //initialize encoder
             System.Text.ASCIIEncoding enc = new ASCIIEncoding();
             //encode the data to a string
             cell = enc.GetString(buffer);
             //dispose view reader
            reader.Dispose();
             //return the cell.
            return cell ;

        }
         ///<summary> 
         /// This method writes the contend of an cell of the matrix, guiven the i and j position.(regular method for any size). 
         ///</summary> 
         ///<param name="posi">
         /// Line position of the desired cell.
         ///</param>
         ///<param name="posj">
         /// Column position of the desired cell.
         ///</param>
         ///<param name="posj">
         /// Data to be stored on the position.
         ///</param>
        public void setPosition(long posi, long posj, String data)
        {
            //verify cell data size.
            if (data.Length > cellsize)
            {
                throw new Exception("Data is bigger than cell size parameter");
            }
            //calculates line offset.
            long linePosition = posi * (tamj * cellsize);
            //calculate column offset.
            long columnPosition = posj * cellsize;
            //calculate cell offset
            long offset = linePosition + columnPosition;
            //initializes the writer acessor
            MemoryMappedViewAccessor writer = mmf.CreateViewAccessor(offset, cellsize + 1);
            //initialize the encoder
            ASCIIEncoding asc = new ASCIIEncoding();
            //encode data into byte array.
            Byte[] bits = asc.GetBytes(data);
            //write the array into the file.
            writer.WriteArray(0, bits, 0, bits.Length);
            //dispose acessor.
            writer.Dispose();

        }

        ///<summary> 
        /// This method writes the contend of an line with contiguous cells of the matrix, guiven the i and the line data.(Best method for a HUGE matrix population) 
        ///</summary> 
        ///<param name="posi">
        /// Line position.
        ///</param>
       ///<param name="data">
        /// Data to be stored on the line.
        ///</param>
        public void setLine(long posi, String data)
        {
            //verify cell data size.
            if (data.Length > cellsize*tamj)
            {
                throw new Exception("Data is bigger than Line size pare mapped");
            }
            //calculates line offset.
            long linePosition = posi * (tamj * cellsize);
            //initializes the writer acessor
            MemoryMappedViewAccessor writer = mmf.CreateViewAccessor(linePosition, (cellsize*tamj)+(long)1);
            //initialize the encoder
            ASCIIEncoding asc = new ASCIIEncoding();
            //encode data into byte array.
            Byte[] bits = asc.GetBytes(data);
            //write the array into the file.
            writer.WriteArray(0, bits, 0, bits.Length);
            //dispose acessor.
            writer.Dispose();

        }

        ///<summary> 
        /// This method writes the contend of an line with contiguous cells of the matrix, guiven the i and the line data.(Best method for a HUGE matrix population) 
        ///</summary> 
        ///<param name="posi">
        /// Line position.
        ///</param>
        ///<param name="posj">
        /// Offset, where to Start to write.
        ///</param>
        ///<param name="data">
        /// Line data to write.
        ///</param>
        public void setLineByOffset(long posi, long posj, String data)
        {
            //verify cell data size.
            if (data.Length > cellsize * tamj)
            {
                throw new Exception("Data is bigger than Line size pare mapped");
            }
            //calculates line offset.
            long linePosition = posi * (tamj * cellsize);
            long columnPosition = posj * cellsize;
            long offset = linePosition + columnPosition;
            //initializes the writer acessor
            MemoryMappedViewAccessor writer = mmf.CreateViewAccessor(offset, (cellsize * (tamj-posj)) + (long)1);
            //initialize the encoder
            ASCIIEncoding asc = new ASCIIEncoding();
            //encode data into byte array.
            Byte[] bits = asc.GetBytes(data);
            //write the array into the file.
            writer.WriteArray(0, bits, 0, bits.Length);
            //dispose acessor.
            writer.Dispose();

        }

        ///<summary> 
        /// This method writes the contend of an cell of the matrix, guiven the i and j position Asyncronously(Good method for small matrix). 
        ///</summary> 
        ///<param name="posi">
        /// Line position of the desired cell.
        ///</param>
        ///<param name="posj">
        /// Column position of the desired cell.
        ///</param>
        ///<param name="posj">
        /// Data to be stored on the position.
        ///</param>
        public void setPositionAsynchronously(long posi, long posj, String data)
        {
            //verify data lenght
            if (data.Length != cellsize)
            {
                throw new Exception("Data must have the same size than cell size parameter");
            }
            //calculates line offset
            long linePosition = posi * ((tamj - 1) * cellsize);
            //calculates the column offset.
            long columnPosition = posj * cellsize;
            //calculates the cell offset
            long offset = linePosition + columnPosition;
            //prepare the parameter list for the thread
            Object[] paramlist = new Object[2];
            //inset the parameters into the list
            paramlist[0] = offset;
            paramlist[1] = data;

            //Start Task
            ThreadPool.QueueUserWorkItem(tAddDados, paramlist);
            

        }
        //this is the asyncronous task
        public void tAddDados(Object Parameters)
        {
            Object[] parametros = Parameters as Object[];
            long offset = (long)parametros[0];
            String data = parametros[1] as String;

            MemoryMappedViewAccessor writer = mmf.CreateViewAccessor(offset, cellsize + 1);
            //initialize the encoder
            ASCIIEncoding asc = new ASCIIEncoding();
            //encode data into byte array.
            Byte[] bits = asc.GetBytes(data);
            //write the array into the file.
            writer.WriteArray(0, bits, 0, bits.Length);
            //dispose acessor.
            writer.Dispose();
            
        }
        ///<summary> 
        /// This method writes the contend of an Blcok of cells , guiven the Block list.. Asyncronously. (Good method for mediu matrix).
        /// Beware of the memmory.
        /// This Block must be an MMFMatrixCell List.
        ///</summary> 
        ///<param name="Block">
        /// Block of cells to be added.
        ///</param>
        public void setBlockPosition(List<mmfMatrixCell> Block)
        {
            
            //pass the block to the task.
            ThreadPool.QueueUserWorkItem(tSetBlockPosition, Block);
            
        }
        public void tSetBlockPosition(Object Parameters) 
        {
            //get the block from parameters
            List<mmfMatrixCell> Block = Parameters as List<mmfMatrixCell>;
            //go into block adding the cells
            foreach (mmfMatrixCell cell in Block)
            {
                this.setPosition((int)cell.posi, (int)cell.posj, cell.data);
            }


        }


        ///<summary> 
        /// This method disposes the mmf file and delete it.
        /// use this method after working with the matrix.
        ///</summary> 
        public void DeleteFile()
        {
            mmf.Dispose();
            File.Delete(fileName);
        }




    }

